package com.hemerasolutions.v2trainer.model.step.substep;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;

import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import com.hemerasolutions.v2trainer.model.base.BaseModel;
import com.hemerasolutions.v2trainer.model.exercise.Exercise;
import com.hemerasolutions.v2trainer.model.exercise.TemplateExercise;
import com.hemerasolutions.v2trainer.model.exercise.executions.Execution;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityExecution;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityModTable;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityModZone;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityTable;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityZone;
import com.hemerasolutions.v2trainer.model.step.Step;
import com.hemerasolutions.v2trainer.model.training.statistic.TrainingStatistic;
import com.hemerasolutions.v2trainer.model.unit.volume.VolumeFactor;
import com.hemerasolutions.v2trainer.model.unit.volume.VolumeType;
import com.hemerasolutions.v2trainer.util.Utility;

@Entity
public class ExerciseStep extends BaseModel  {

	private static final long serialVersionUID = 1L;

    @Column
    private double value;
    
    @ManyToOne
    private Exercise exercise;
    
    @ManyToOne
    private Execution execution;

    @ManyToOne
    private VolumeFactor volumeFactor;
    
    @ManyToOne
    private VolumeType volumeType;

    @ManyToOne
    private IntensityZone intensityZone;

    @OneToOne
    private Step step;
    
	public ExerciseStep() {
		super();
	}

	public ExerciseStep(ExerciseStep other, Step parent) {
		super();
		this.id = null;
		this.execution = other.execution;
		this.exercise = other.exercise;
		this.step = parent;
		this.value = other.value;
		this.volumeFactor = other.volumeFactor;
		this.volumeType = other.volumeType;

		if(other.getStep().getTrainingStep().getLevel() == parent.getTrainingStep().getLevel()){
			this.intensityZone = other.intensityZone;
		}else{
			this.intensityZone = this.adaptIz(exercise,execution,other.getStep().getTrainingStep().getLevel(),parent.getTrainingStep().getLevel(),other.intensityZone);
		}

	
	}

	private IntensityZone adaptIz(Exercise exercise, Execution ex, int levelOrig, int levelDest,
			IntensityZone izOrig) {
		
		IntensityTable itOrig = null;
		IntensityTable itDest = null;
		
		
		for(IntensityExecution ie:exercise.getIntensityExecutions()){
			if(ie.getExecution().equals(ex)){
				for(IntensityTable it:ie.getIntensityTables()){
					if(it.getLevel() == levelOrig){
						itOrig = it;
					}else if (it.getLevel() == levelDest){
						itDest = it;
					}
				}
				break;
			}
		}
		
		if(itOrig != null && itDest !=null){
			int index = itOrig.getIntensitys().indexOf(izOrig);
			if(itDest.getIntensitys().size() > index){
				return itDest.getIntensitys().get(index);
			}
		}
		
		return null;
	}

	public ExerciseStep( Step parent) {
		super();
		this.id = null;
		this.step = parent;
	}
	
	public ExerciseStep(ExerciseGroupedStep other, Step parent) {
		super();
		this.id = null;
		this.execution = other.getStep().getParentStep().getExerciseGroupStep().getExecution();
		this.exercise = other.getExercise();
		this.intensityZone = other.getIntensityZone();
		this.step = parent;
		this.value = other.getStep().getParentStep().getExerciseGroupStep().getValue();
		this.volumeFactor = other.getStep().getParentStep().getExerciseGroupStep().getVolumeFactor();
		this.volumeType = other.getStep().getParentStep().getExerciseGroupStep().getVolumeType();
	}

	public ExerciseStep(TemplateExercise te,Exercise exercise,Step step){
		super();
		this.id = null;
		this.step = step;
		this.exercise = exercise ;
		this.volumeType = te.getVolumeType();
		this.volumeFactor = te.getVolumeFactor();
		this.intensityZone = te.getIntensityZone(exercise);
		this.setValue(te.getValue());
		
		if(exercise.getExecutions().contains(te.getExecution())){
			this.execution = te.getExecution();
		}
		
	}
	
	
	public TrainingStatistic calcStatistic() {
		
		TrainingStatistic ts = new TrainingStatistic(this.step.getTrainingStep(),TrainingStatistic.EXERCISE_STATISTIC);
		long totalRest   = this.calculateRestTime();
		long totalAction = this.calculateExpentTime();

		

		ts.setActionTime(Utility.longToDate(totalAction));
		ts.setRestTime(Utility.longToDate(totalRest));
		ts.setTotalTime(Utility.longToDate(totalAction + totalRest));
		ts.setExecution(this.execution);
		ts.setVolumeFactor(this.volumeFactor);
		ts.setVolumeType(this.volumeType);
		ts.setRealValue(this.value);
		ts.setExercise(this.exercise);
		ts.setIntensityZone(this.intensityZone);
		ts.calcStatistics();
		
		return ts;
		
	}	
	
	public long calculateRestTime(){
		if(this.step.getRepetition() > 2){
			return Utility.dateToLong(step.getRestInterval()) * (this.step.getRepetition() - 1);
		}else{
			return Utility.dateToLong(step.getRestInterval());
		}
	}
	
	private long calculateExpentTime() {
	  	long time = 0;
    	String ivName = this.exercise.getModality().getTrainingType().getIntensityVolume().getName();
    	String vtName = this.volumeType.getName();
    	
    	if(intensityZone != null){
    		if(ivName.equals("V_TIME")){

    			time += Utility.dateToLong(intensityZone.getConcentricTime());
    			time += Utility.dateToLong(intensityZone.getEccentricTime());

    		}else if (ivName.equals("V_DISTANCE")){
    			if(this.volumeType.getName().equals("V_TIME")){
    				time += Utility.dateToLong(this.getDateValue());
    			}else if(vtName.equals("V_DISTANCE")){
    				if(this.intensityZone.getRealAverageSpeed() > 0){
        				time += (long)(this.value / this.intensityZone.getRealAverageSpeed());
        				time *= 1000;
    				}
    			}
    		}else{
    			if(vtName.equals("V_TIME")){
    				time += Utility.dateToLong(this.getDateValue());
    			}
    		}
    	}

		time *= step.getRepetition();

    	
    	if(vtName.equals("V_REPETITION")){
			time *= this.value;
    	}
    	
    	return time;  

	}
	
	public IntensityModZone getIntensityModZone() {
		for(IntensityModTable imt: exercise.getModality().getIntensityModTables()){
			if(imt.getExecution().equals(this.execution)){
				for(IntensityModZone imz:imt.getIntensityModZones()){
					if(imz.getName().equals(intensityZone.getName())){
						return imz;
					}
				}
				for(IntensityModZone imz:imt.getIntensityModZones()){
					if(imz.getPosition() == intensityZone.getPosition()){
						return imz;
					}
				}
			}
		}
		return null;
	}		
	
	public String getTranslatedExercise(){
		String desc = "";
		String restTime = Utility.timeToString(this.step.getRestInterval());

		
		desc = this.step.getRepetition() > 1 ? 	" " + this.step.getRepetition() +"x ":" ";
		
		try{
			if(this.getVolumeFactor().isTimeBased()){
				desc += this.getIntervalToString() + " "  + this.exercise.getName() + " " + this.intensityZone.getName();		
			}else{
				desc += this.getStringValue() + " " + Utility.getText(this.getVolumeFactor().getName()) + " " + this.exercise.getName() + " " +
						this.intensityZone.getName();			
			}

			if(!restTime.equals("")){
				desc += " " + "ID: " + restTime;
			}		
		
		}catch (Exception e) {
			
		}
		return desc;
	}
	
	public String getStringValue(){
		return this.getVolumeFactor().format(this.getValue());
	}
	
	public Exercise getExercise() {
		return exercise;
	}

	public void setExercise(Exercise exercise) {
		this.exercise = exercise;
	}

	public Execution getExecution() {
		return execution;
	}

	public void setExecution(Execution execution) {
		this.execution = execution;
	}

	public VolumeType getVolumeType() {
		return volumeType;
	}

	public void setVolumeType(VolumeType volumeType) {
		this.volumeType = volumeType;
	}

	public IntensityZone getIntensityZone() {
		return intensityZone;
	}

	public void setIntensityZone(IntensityZone intensiyZone) {
		this.intensityZone = intensiyZone;
	}


	public Step getStep() {
		return step;
	}

	public void setStep(Step step) {
		this.step = step;
	}

	public double getValue() {
		if(this.volumeFactor != null && value != 0 ){
			EvaluationContext context 	= new StandardEvaluationContext(); 
			ExpressionParser parser 	= new SpelExpressionParser();
			Expression exp = null;

		
			context.setVariable("value", value);
			exp = parser.parseExpression( this.volumeFactor.getUpFormula());
			
			return (double) exp.getValue(context);
		}else{
			return value ;
		}
	}

	public void setValue(double value) {
		if(this.volumeFactor != null && value != 0 ){
			EvaluationContext context 	= new StandardEvaluationContext(); 
			ExpressionParser parser 	= new SpelExpressionParser();
			Expression exp = null;
			 
			context.setVariable("value", value);
			exp = parser.parseExpression( this.volumeFactor.getDownFormula());
			
			this.value = (double) exp.getValue(context);			
		}else{
			this.value = value  ;
		}
	}	
	
	public Date getDateValue() {

		Calendar c = Calendar.getInstance();
		c.setTimeZone(TimeZone.getTimeZone("Etc/GMT0"));

		if(value != 0){
			c.setTimeInMillis((long)this.getValue()*1000);
		}else{
			c.setTimeInMillis(0);
		}

		return c.getTime();
	}

	public void setDateValue(Date dateValue) {

		Calendar c = Calendar.getInstance();
		c.setTimeZone(TimeZone.getTimeZone("Etc/GMT0"));

		if(dateValue != null){
			c.setTime(dateValue);
			if(c.getTimeInMillis() == 0){
				this.value = 0;
			}else{
				this.setValue((double)c.getTimeInMillis()/1000);
			}
		}else{
			this.value = 0;
		}
	}


	public VolumeFactor getVolumeFactor() {
		return volumeFactor;
	}

	public void setVolumeFactor(VolumeFactor volumeFactor) {
		this.volumeFactor = volumeFactor;
	}

	public String getIntervalToString(){
		return Utility.timeToString(this.getDateValue());
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((step == null) ? 0 : step.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ExerciseStep other = (ExerciseStep) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (step == null) {
			if (other.step != null)
				return false;
		} else if (!step.equals(other.step))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "ExerciseStep [id=" + id + ", step=" + step + "]";
	}
}
